#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "simple_linked_list.h"

/* 
 * Private Methods
 *
 * */

void vPrvSimLL_DeallocNode( xpSimpleNodeHandle node )
{
    /* Release Node Data Reference */
    free( node->vpNodeData );

    /* Release the Node Reference */
    free( node );
}

/*
 * Public Methods
 *
 * */

xpSimpleListHandle xpSimLL_CreateList( int size, int length )
{
  /* Allocate memory for a new list */
  xpSimpleListHandle list_buff = (xpSimpleListHandle)malloc( sizeof(xConcreteList) );

  /* Set list node size */
  list_buff->iNodeSize = size;
  list_buff->iMaxListLength = length;

  list_buff->xpMutex = malloc( sizeof( pthread_mutex_t ) );

  pthread_mutex_init( list_buff->xpMutex, NULL );

  return list_buff;
}

void vSimLL_DestroyList( xpSimpleListHandle list )
{
  vSimLL_FlushList( list );

  pthread_mutex_destroy( list->xpMutex );

  free( list->xpMutex );

  free( list );
}

int iSimLL_AddNodeToList( xpSimpleListHandle list, void * input_node )
{
  xpSimpleNodeHandle currentNode, newNode;
  int iReturnValue = 0;

  if( ( list->iListLength < list->iMaxListLength ) || ( -1 != list->iListLength ) )
  {
    /* Lock List so there are no other additions */
    pthread_mutex_lock( list->xpMutex );

    /* Create Node Reference */
    newNode = (xpSimpleNodeHandle)malloc( sizeof( xConcreteNode ) );

    /* Create Node Data Reference */
    newNode->vpNodeData = malloc( sizeof( list->iNodeSize ) );

    /* Clear the new nextNode */
    newNode->xpNextNode = NULL;

    /* Check to see if Memory is avaiable */
    if( NULL == newNode )
    {
      printf("ERROR: No Memory available\n");
      iReturnValue = 1;
    }
    else
    {
      /* Copy Input into New Node's Data Reference */
      memcpy( newNode->vpNodeData, input_node, list->iNodeSize );
    }

    /* Get the base node in the list */
    currentNode = list->xpListBase;

    /* Check to if the node exists */
    if( NULL == currentNode )
    {
      /* Assign the base node */
      list->xpListBase = newNode;
    }
    else
    {
      /* Find the end of the list */
      while( NULL != currentNode->xpNextNode )
      {
        /* Increment Node Pointer */
        currentNode = currentNode->xpNextNode;
      }

      /* Set the Next Node Reference */
      currentNode->xpNextNode = newNode;
    }

    /* Increment List Counter */
    list->iListLength++;

    /* Lock List so there are no other additions */
    pthread_mutex_unlock( list->xpMutex );
  }
  else
  {
    printf( "List is Full!!! size: %d, length: %d\n", list->iListLength, list->iMaxListLength );
    iReturnValue = 1;
  }

  return iReturnValue;
}

int vSimLL_ReadNode( xpSimpleListHandle list, void * data, int position )
{
  int loop, iReturnValue = 0;
  xpSimpleNodeHandle currentNode;

  /* Lock List so there are no other additions */
  pthread_mutex_lock( list->xpMutex );

  /* Need to account for enumerating at zero */
  if( ( position + 1 ) > list->iListLength )
  {
    iReturnValue = 1;
  }
  else
  {
    currentNode = list->xpListBase;

    for( loop = 0; loop < position; loop++ )
    {
      /* Move the Current Node Pointer */
      currentNode = currentNode->xpNextNode;

      /* Check to see if the Node exists */
      if( NULL == currentNode ) break;
    }

    /* Verify the node exist before tryin to dereference it */
    if( NULL != currentNode )
    {
      /* Copy Data out */
      memcpy( data, currentNode->vpNodeData, list->iNodeSize );
    }
    else
    {
      /* Tell user that there is nothing to load at the position */
      data = NULL;
    }
  }

  /* Lock List so there are no other additions */
  pthread_mutex_unlock( list->xpMutex );

  return iReturnValue;
}

void vSimLL_RemoveNode( xpSimpleListHandle list, void * data, int position )
{
  int loop;

  xpSimpleNodeHandle currentNode, previousNode;

  /* Lock List so there are no other additions */
  pthread_mutex_lock( list->xpMutex );

  /* Get List Base */
  currentNode = list->xpListBase;

  for( loop = 0; loop < position; loop++ )
  {
    /* Find an empty node */
    if( NULL == currentNode ) break;

    /* Check to see if the next node is empty */
    if( NULL == currentNode->xpNextNode )
    {
      /* Set Current Node to NULL, so nothing is returned */
      currentNode = NULL; 
    }
    else
    {
      /* Set Previous Node */
      previousNode = currentNode;
      /* Goto Next Node */
      currentNode = currentNode->xpNextNode;
    }
  }

  if( NULL == currentNode )
  {
    /* Do Nothing */
  }
  else
  {
    /* Set Previous Node's Next Reference */
    previousNode->xpNextNode = currentNode->xpNextNode;
    
    /* Copy Data from Leaving Node */
    memcpy( data, currentNode->vpNodeData, list->iNodeSize );

    /* Remove Node from Memeory */
    vPrvSimLL_DeallocNode( currentNode );

    /* Decrement List Counter */
    list->iListLength--;
  }

  pthread_mutex_unlock( list->xpMutex );
}

int iSimLL_GetAndRemoveFirstNode( xpSimpleListHandle list, void * return_buff )
{
  xpSimpleNodeHandle currentNode;
  int iReturnValue = 0;

  /* Lock List so there are no other additions */
  pthread_mutex_lock( list->xpMutex );

  currentNode = list->xpListBase;

  if( NULL == currentNode )
  {
    /* Do nothing, list is empty */
    iReturnValue = 1;
  }
  else
  {
    /* Copy return value */
    memcpy( return_buff, currentNode->vpNodeData, list->iNodeSize ); 

    /* Check to see if there is another node in the list */
    if( NULL == currentNode->xpNextNode )
    {
      /* Signify empty list */
      list->xpListBase = NULL;
    }
    else
    {
      /* Shift list base */
      list->xpListBase = currentNode->xpNextNode;
    }

    /* Remove Node from Memory */
    vPrvSimLL_DeallocNode( currentNode );

    /* Decrement List Counter */
    list->iListLength--;
  }

  pthread_mutex_unlock( list->xpMutex );

  return iReturnValue;
}

int iSimLL_GetListLength( xpSimpleListHandle list )
{
  int iReturnValue;

  /* Lock List so there are no other additions */
  pthread_mutex_lock( list->xpMutex );

  /* Return the List Counter */
  iReturnValue = list->iListLength;

  pthread_mutex_unlock( list->xpMutex );

  return iReturnValue;
}

void vSimLL_FlushList( xpSimpleListHandle list )
{
  int iLoop, iListLength = list->iListLength;
  xpSimpleNodeHandle currentNode;

  /* Lock List so there are no other additions */
  pthread_mutex_lock( list->xpMutex );

  for( iLoop = 0; iLoop < iListLength; iLoop++ )
  {
    /* Filter out premature ending */
    if( NULL != list->xpListBase )
    {
      currentNode = list->xpListBase;

      /* Check to see if there is another node in the list */
      if( NULL == currentNode->xpNextNode )
      {
        /* Signify empty list */
        list->xpListBase = NULL;
      }
      else
      {
        /* Shift list base */
        list->xpListBase = currentNode->xpNextNode;
      }

      /* Remove Node from Memory */
      vPrvSimLL_DeallocNode( currentNode );
    }
    else
    {
      break;
    }
  }

  pthread_mutex_unlock( list->xpMutex );
}
